import torch

def angular_loss(pred, target, z_screen=866.1):
    """
    角度损失函数（角度单位为度），坐标原点为左上角
    :param pred: (N, 2) 预测 gaze 坐标 (x, y)，左上角为(0,0)
    :param target: (N, 2) 真实 gaze 坐标 (x, y)，左上角为(0,0)
    :param z_screen: 屏幕距离
    :return: 标量损失（均方角度误差，单位为度）
    """
    # 构造三维向量，左上角为原点
    pred_vec = torch.cat([pred, torch.full((pred.shape[0], 1), z_screen, device=pred.device)], dim=1)  # (N, 3)
    target_vec = torch.cat([target, torch.full((target.shape[0], 1), z_screen, device=target.device)], dim=1)  # (N, 3)
    # 归一化
    pred_vec = pred_vec / pred_vec.norm(dim=1, keepdim=True)
    target_vec = target_vec / target_vec.norm(dim=1, keepdim=True)
    # 计算夹角（弧度）
    dot = (pred_vec * target_vec).sum(dim=1).clamp(-1.0, 1.0)
    theta_rad = torch.acos(dot)
    # 转换为度
    theta_deg = theta_rad * (180.0 / torch.pi)
    # 均方角度损失（度数制）
    loss = torch.mean(theta_deg ** 2)
    return loss